//! sky自创语言的词法处理流程

use super::token::Token;
use crate::{lexer::Observe, Error, Result};
use std::{collections::HashMap, fmt::Debug};

pub const ILLEGAL: i32 = -2;
pub const EOF: i32 = -1;
pub const LINE_DOC: i32 = -3;

// -----关键字-----------------------
pub const LET: i32 = 110;
pub const IF: i32 = 111;
pub const ELSE: i32 = 112;
pub const RETURN: i32 = 113;
pub const FUNCTION: i32 = 114;
pub const NULL: i32 = 115;

// -----操作符-------------------------
pub const BAND: i32 = 200;
pub const MINUS: i32 = 201;
pub const PLUS: i32 = 202;
pub const EQUAL_SIGN: i32 = 203;
pub const EQ: i32 = 204;
pub const NOT_EQ: i32 = 205;
pub const LT: i32 = 206;
pub const GT: i32 = 207;
pub const SLASH: i32 = 208;
pub const ASTERISK: i32 = 209;
pub const PARENTHESIS_LEFT: i32 = 210;
pub const PARENTHESIS_RIGHT: i32 = 211;
pub const SQUARE_BRACKET_LEFT: i32 = 212;
pub const SQUARE_BRACKET_RIGHT: i32 = 213;
pub const BRACE_LEFT: i32 = 214;
pub const BRACE_RIGHT: i32 = 215;

/// 表达式
pub const TRUE: i32 = 300;
pub const FALSE: i32 = 301;
pub const IDENTIFIER: i32 = 302;
pub const INTEGER: i32 = 303;
pub const STRING: i32 = 304;

/// 标点
pub const COMMA: i32 = 400;
pub const SEMICOLON: i32 = 401;
pub const COLON: i32 = 402;

macro_rules! generate_token {
    ($t:expr, $literal:expr, $edit:expr) => {{
        $edit.read_position += 1;
        Ok(Token::new(
            $t,
            $literal,
            $edit.line_count,
            $edit.position,
            $edit.read_position - 1,
        ))
    }};
}

#[derive(Debug, Clone)]
pub struct Lexer<T = DefaultObserve>
where
    T: Observe + Clone,
    <T as Observe>::Context: Clone + Debug,
{
    source_code: String,
    source_code_len: usize,
    position: usize,
    read_position: usize,
    line_count: usize,
    keywords: HashMap<String, Token>,
    observer: Option<T>,
    context: Option<<T as Observe>::Context>,
}

impl<T> Lexer<T>
where
    T: Observe + Clone,
    <T as Observe>::Context: Clone + Debug,
{
    /// 词法分析，生成最终的Token数组
    pub fn lexing(&mut self) -> Result<Vec<Token>> {
        // 这里首先读取一下，是为了防止衔接下面的skip_whitespace_and_new_line函数，让他去判断读取到的字符
        let mut tokens = vec![];
        while let token = self.next_token()? {
            //println!("{}", token);
            if token.r#type() == LINE_DOC {
                continue;
            }
            if token.r#type() == EOF {
                tokens.push(token.clone());
                break;
            }
            tokens.push(token.clone());
        }
        Ok(tokens)
    }

    /// 读取字符， 从read_position这个位置读取一个字符
    pub fn read_char(&self) -> char {
        // 如果read_position大于等于字符串的长度，代表没有字符可以读取了，到了末尾了。
        if self.read_position >= self.source_code_len {
            // 此时设置读取到的字符是 '\0'
            '\0'
        } else {
            // 如果没有到达末尾，那么读取这个字符到当前的ch字段中。
            self.source_code.chars().nth(self.read_position).unwrap()
        }
    }

    pub fn init_keywords(&mut self) {
        self.keywords
            .insert("let".into(), Token::new(LET, "let", 0, 0, 0));
        self.keywords
            .insert("if".into(), Token::new(IF, "if", 0, 0, 0));
        self.keywords
            .insert("else".into(), Token::new(ELSE, "else", 0, 0, 0));
        self.keywords
            .insert("return".into(), Token::new(RETURN, "return", 0, 0, 0));
        self.keywords
            .insert("fn".into(), Token::new(FUNCTION, "fn", 0, 0, 0));
        self.keywords
            .insert("null".into(), Token::new(NULL, "null", 0, 0, 0));
    }

    /// 忽略空格，或者换行
    pub fn skip_whitespace_and_new_line(&mut self) {
        // 下面都认为都是无用数据
        let mut get_char = self.read_char();
        while get_char == ' ' || get_char == '\t' || get_char == '\n' {
            // 如果是换行，line_count要增加1
            if get_char == '\n' {
                self.line_count += 1;
            }
            // 如果为空，那么重新定位position
            self.read_position += 1;
            self.position = self.read_position;
            // 是无用数据，则要不停的读取下一个字符，一直循环到不是空格为止。
            get_char = self.read_char();
        }
    }

    /// 生成下一个token
    pub fn next_token(&mut self) -> Result<Token> {
        // 除去空格直到是一个有效的数据
        self.skip_whitespace_and_new_line();
        // 记录当前的行号
        let line_count = self.line_count;
        // 下面就是判断当前的字符
        let tok = match self.read_char() {
            '"' => {
                let str = self.read_string();
                if let Ok(str) = str {
                    generate_token!(STRING, &str, self)
                } else {
                    generate_token!(ILLEGAL, "undefined", self)
                }
            }
            '=' => {
                if self.eq_eq_check() {
                    generate_token!(EQ, "==", self)
                } else {
                    generate_token!(EQUAL_SIGN, "=", self)
                }
            }
            '!' => {
                if self.not_eq_check() {
                    generate_token!(NOT_EQ, "!=", self)
                } else {
                    generate_token!(BAND, "!", self)
                }
            }
            ';' => {
                generate_token!(SEMICOLON, ";", self)
            }
            ':' => {
                generate_token!(COLON, ":", self)
            }
            '+' => {
                generate_token!(PLUS, "+", self)
            }
            '-' => {
                generate_token!(MINUS, "-", self)
            }
            ',' => {
                generate_token!(COMMA, ",", self)
            }
            '{' => {
                generate_token!(BRACE_LEFT, "{", self)
            }
            '}' => {
                generate_token!(BRACE_RIGHT, "}", self)
            }
            '(' => {
                generate_token!(PARENTHESIS_LEFT, "(", self)
            }
            ')' => {
                generate_token!(PARENTHESIS_RIGHT, ")", self)
            }
            '[' => {
                generate_token!(SQUARE_BRACKET_LEFT, "[", self)
            }
            ']' => {
                generate_token!(SQUARE_BRACKET_RIGHT, "]", self)
            }
            '<' => {
                generate_token!(LT, "<", self)
            }
            '>' => {
                generate_token!(GT, ">", self)
            }
            '/' => {
                self.read_position += 1;
                if self.read_char() == '/' {
                    //是行注释
                    let doc = self.release_doc_line();
                    generate_token!(LINE_DOC, &doc, self)
                } else {
                    self.read_position -= 1;
                    generate_token!(SLASH, "/", self)
                }
            }
            '*' => {
                generate_token!(ASTERISK, "*", self)
            }

            // 如果是结尾，就不需要提前read_char()了，直接返回EOF的Token。
            '\0' => {
                generate_token!(EOF, "eof", self)
            }

            // 都不是上面的符号，那么就去读取统一的标识
            _ => self
                .read_identifier()
                .and_then(|v| {
                    // 读取标识，判断得到的标识是什么类型的标识， 关键字还是普通的标识
                    if self.keywords.get(&v).is_some() {
                        let mut t = self.keywords[&v].clone();
                        t.set_begin(self.position);
                        t.set_end(self.read_position - 1);
                        t.set_line_num(line_count);
                        Some(t)
                    } else if &v == "true" {
                        Some(Token::new(
                            TRUE,
                            &v,
                            line_count,
                            self.position,
                            self.read_position - 1,
                        ))
                    } else if &v == "false" {
                        Some(Token::new(
                            FALSE,
                            &v,
                            line_count,
                            self.position,
                            self.read_position - 1,
                        ))
                    } else {
                        Some(Token::new(
                            IDENTIFIER,
                            &v,
                            line_count,
                            self.position,
                            self.read_position - 1,
                        ))
                    }
                })
                .or_else(|| {
                    // 上面read_identifier()返回了空，表示不是标识符，此时去读取数字
                    self.read_number().and_then(|v| {
                        Some(Token::new(
                            INTEGER,
                            &v,
                            line_count,
                            self.position,
                            self.read_position - 1,
                        ))
                    })
                })
                // 既不是标识，也不是数字，那么返回出错的信息。
                .ok_or(format!("line: {} 编译失败!", line_count).into()),
        };
        tok.map(|tok| {
            self.notify_observer(tok.clone());
            tok
        })
    }

    pub fn notify_observer(&mut self, tok: Token) {
        if let Some(obs) = &self.observer {
            if let Some(cxt) = &self.context {
                obs.clone().notify_token_creation(tok, cxt.clone());
            }
        }
        self.position = self.read_position;
    }

    /// 读取数字类的字面字符串,这里我们默认Integer全部都是f64位的
    pub fn read_number(&mut self) -> Option<String> {
        let mut dot = false;
        let mut number = String::default();
        let mut get_char = self.read_char();
        loop {
            if Self::is_number(get_char) {
                number.push(get_char);
                self.read_position += 1;
                get_char = self.read_char();
                continue;
            }
            if Self::is_dot(get_char) && dot == false {
                number.push(get_char);
                self.read_position += 1;
                get_char = self.read_char();
                dot = true;
                continue;
            }
            break;
        }
        if !number.is_empty() {
            Some(number)
        } else {
            None
        }
    }

    fn is_dot(c: char) -> bool {
        c == '.'
    }

    /// 判断是否是字符
    pub fn is_letter(ch: char) -> bool {
        let tmp = ch as u32 >> 8;
        (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z' || ch == '_') || (tmp > 0)
    }

    pub fn is_number(ch: char) -> bool {
        ch >= '0' && ch <= '9'
    }

    /// 循环读取字符，得出一个标识
    pub fn read_identifier(&mut self) -> Option<String> {
        let mut identifier = String::default();
        // 如果读取到的字符不是一个letter那么结束循环， 得到一个字符串
        let mut get_char = self.read_char();
        let mut yes = false;
        loop {
            if Self::is_letter(get_char) {
                identifier.push(get_char);
                self.read_position += 1;
                get_char = self.read_char();
                yes = true;
                continue;
            }
            if Self::is_number(get_char) && yes {
                identifier.push(get_char);
                self.read_position += 1;
                get_char = self.read_char();
                continue;
            }
            break;
        }

        // 如果返回为空，表示出现了错误
        if !identifier.is_empty() {
            Some(identifier)
        } else {
            None
        }
    }

    //----------------------get info -----------------------------------------------
    pub fn source_code(&self) -> String {
        self.source_code.clone()
    }
    pub fn position(&self) -> usize {
        self.position
    }
    pub fn read_position(&self) -> usize {
        self.read_position
    }
    pub fn line_count(&self) -> usize {
        self.line_count
    }
    pub fn keywords(&self) -> HashMap<String, Token> {
        self.keywords.clone()
    }

    pub fn eq_eq_check(&mut self) -> bool {
        self.read_position += 1;
        let peek_char = self.read_char();
        if peek_char == '=' {
            return true;
        }
        self.read_position -= 1;
        false
    }

    pub fn not_eq_check(&mut self) -> bool {
        self.read_position += 1;
        if self.read_char() == '=' {
            return true;
        }
        self.read_position -= 1;
        false
    }

    fn read_string(&mut self) -> Result<String> {
        self.read_position += 1;
        let mut str = String::default();
        let mut tmp = '\0';
        loop {
            tmp = self.read_char();
            if tmp != '"' && tmp != '\0' {
                str.push(tmp);
                self.read_position += 1;
                continue;
            }
            break;
        }
        if tmp != '"' {
            return Err(Error::from("字符串丢失双引号"));
        }
        Ok(str)
    }

    fn release_doc_line(&mut self) -> String {
        let mut tmp = '\0';
        self.read_position += 1;
        let mut ret = String::default();
        loop {
            tmp = self.read_char();
            if tmp == '\0' || tmp == '\n' {
                self.read_position -= 1;
                break;
            }
            ret.push(tmp);
            self.read_position += 1;
        }
        ret
    }
}

pub fn is_eof(tok: &Token) -> bool {
    tok.r#type() == EOF
}

pub fn is_identifier(tok: &Token) -> bool {
    tok.r#type() == IDENTIFIER
}

pub fn is_punct(tok: &Token) -> bool {
    tok.r#type() == EQUAL_SIGN
        || tok.r#type() == PLUS
        || tok.r#type() == SEMICOLON
        || tok.r#type() == COMMA
        || tok.r#type() == SQUARE_BRACKET_RIGHT
        || tok.r#type() == SQUARE_BRACKET_LEFT
        || tok.r#type() == BRACE_LEFT
        || tok.r#type() == BRACE_RIGHT
        || tok.r#type() == PARENTHESIS_LEFT
        || tok.r#type() == PARENTHESIS_RIGHT
}

pub fn is_integer(tok: &Token) -> bool {
    tok.r#type() == INTEGER
}

pub struct LexerBuilder<T = DefaultObserve>
where
    T: Observe + Clone,
    <T as Observe>::Context: Clone + Debug,
{
    inner: Lexer<T>,
}

impl<T> LexerBuilder<T>
where
    T: Observe + Clone,
    <T as Observe>::Context: Clone + Debug,
{
    pub fn new() -> Self {
        Self {
            inner: Lexer {
                source_code: "".to_string(),
                source_code_len: 0,
                position: 0,
                read_position: 0,
                line_count: 0,
                keywords: Default::default(),
                observer: None,
                context: None,
            },
        }
    }

    pub fn set_source_code(mut self, source_code: &str) -> Self {
        self.inner.source_code = source_code.to_string();
        self.inner.source_code_len = source_code.chars().count();
        self
    }

    pub fn set_position(mut self, position: usize) -> Self {
        self.inner.position = position;
        self
    }

    pub fn set_read_position(mut self, read_position: usize) -> Self {
        self.inner.read_position = read_position;
        self
    }

    pub fn set_line_count(mut self, line_count: usize) -> Self {
        self.inner.line_count = line_count;
        self
    }

    pub fn set_observer(mut self, obs: T, ctx: <T as Observe>::Context) -> Self {
        self.inner.observer = Some(obs);
        self.inner.context = Some(ctx);
        self
    }

    pub fn build(mut self) -> Lexer<T> {
        self.inner.init_keywords();
        self.inner
    }
}

#[derive(Debug, Clone)]
pub struct DefaultObserve;

impl Observe for DefaultObserve {
    type Context = ();

    fn notify_token_creation(self, _tok: Token, _cxt: Self::Context) {}
}
